import traceback
from collections import OrderedDict
from . import middleware
from . import processor
from .packet import Packet


class Connection:
    _connections = OrderedDict()

    def __init__(self, server, sock):
        self.sock = sock
        self.server = server
        self.serializer = None
        self._sessions = {}
        self._disconnect_callbacks = []
        self._connections[self.sock.fileno()] = self
        self._query_dict = OrderedDict()

    def __setitem__(self, key, value):
        self._query_dict[key] = value

    def __getitem__(self, item):
        return self._query_dict[item]

    def __delitem__(self, key):
        if key in self._query_dict:
            del self._query_dict[key]

    def __contains__(self, item):
        return item in self._query_dict

    @property
    def disconnect_callbacks(self):
        return self._disconnect_callbacks

    def close(self):
        try:
            if self.sock.fileno() in self._connections:
                del self._connections[self.sock.fileno()]
            for cb in self._disconnect_callbacks:
                cb(self)
            return self
        except Exception as _:
            traceback.print_exc()

    @property
    def id(self):
        return self.sock.fileno()

    def process_data(self, data):
        if self.serializer:
            pkt = Packet(data, serializer_name=self.serializer)
        else:
            pkt = Packet(data)
            self.serializer = pkt.serializer

        if not pkt:
            return

        for m in middleware.middlewares:
            try:
                m.pre_process(self, pkt)
            except middleware.PacketReject:
                return

        processor.processor.put(self, pkt)

    def route(self, pkt):
        for m in middleware.middlewares:
            try:
                m.post_process(self, pkt)
            except middleware.PacketReject:
                return

        self.server.send_in_queue(self.sock, pkt.to_bytes(serializer_name=self.serializer))

    def get_id(self):
        pass

    @classmethod
    def get_connection_by_sockfd(cls, key):
        if key in cls._connections:
            return cls._connections[key]
        return None

    @classmethod
    def get_connection(cls, conn_id):
        if conn_id in cls._connections:
            return cls._connections[conn_id]
        return None

    def disconnect(self):
        self.server.close(self.sock)
        